(* Copyright (C) 2010 School of Software Engineering, 
 * University of Science and Technology of China.
 *
 * This code is released under a BSD-style license.
 * See the file LICENSE for details.
 *)

structure Layout:> LAYOUT =
struct

datatype t
  = T of {obj: objt, size: int}
     and objt
       = Empty
       | Str of string
       | Seq of t list
       | Align of t list
       | Indent of t * int
                   
fun empty () = T {obj = Empty, 
                  size = 0}
               
fun str s = T {obj = Str s,
               size = String.size s}
            
fun seq ts = 
    let val sz = List.foldl (fn (T {obj, size}, i) =>
                                i+size) 
                            0
                            ts
    in  T {obj = Seq ts,
           size = sz}
    end
    
fun align ts =
    let val sz = List.foldl (fn (T {obj, size}, i) =>
                                if size>i
                                then size
                                else i) 
                            0
                            ts
    in  T {obj = Align ts,
           size = sz}
    end
    
fun indent (t as (T {obj = ob, size = s}), offset) = 
    T {obj = Indent (t, offset), size = s}
    
fun toString t =
    let fun spaces n =
            let fun doit (n, accum) =
                    if n<0
                    then raise Fail "bug"
                    else if n=0
                    then accum 
                    else doit (n-1, String.concat [" ", accum])
            in  doit (n, "")
            end
        fun doit (start, current, accum, T {obj, size}) =
            case obj
             of Empty => (start, current, accum)
              | Str s => (current+size, current+size, 
                          String.concat [accum, spaces (current-start), s])
              | Seq ss => 
                let val (newStart, newCurrent, newAccum) =
                        List.foldl (fn (x, (s, c, a)) =>
                                       doit (s, c, a, x))
                                   (start, current, accum)
                                   ss
                in  (newStart, newCurrent, newAccum)
                end
              | Align ss =>
                let fun cook (ss, accum) =
                        case ss
                         of [] => (start, current, accum)
                          | [x] => doit (start, current, accum, x)
                          | x::xs =>
                            let val (st, cu, ac) = doit (start, current, accum, x)
                            in  cook (xs, String.concat [ac, "\n", spaces start])
                            end
                in  cook (ss, accum)
                end
              | Indent (t, offset) => doit (start, current+offset, accum, t)
    in  #3 (doit (0, 0, "", t))
    end
    
val print = TextIO.print o toString

end
